# -*- coding: utf-8 -*-
"""
全局异常处理器
提供统一的异常处理和响应格式
"""

from fastapi import Request, HTTPException
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
from typing import Union, Optional, Dict, Any, Awaitable
from datetime import datetime
from enum import Enum
import traceback
from src.utils.logger import app_logger as logger


class ResponseCode(Enum):
    """响应状态码枚举"""

    # 成功响应
    SUCCESS = (200, "操作成功")

    # 客户端错误 4xx
    FAILED = (400, "操作失败")
    PARAMS_VALID_ERROR = (400001, "参数验证失败")
    PARAMS_MISSING_ERROR = (400002, "缺少必要参数")
    PARAMS_TYPE_ERROR = (400003, "参数类型错误")
    PARAMS_VALUE_ERROR = (400004, "参数值错误")

    # 认证和授权错误 401/403
    TOKEN_INVALID = (401001, "Token无效或已过期")
    TOKEN_MISSING = (401002, "缺少Token")
    TOKEN_EXPIRED = (401003, "Token已过期")
    LOGIN_REQUIRED = (401004, "请先登录")
    NO_PERMISSION = (403001, "权限不足")
    RESOURCE_FORBIDDEN = (403002, "资源访问被禁止")

    # 资源错误 404
    REQUEST_404_ERROR = (404001, "请求的资源不存在")
    USER_NOT_FOUND = (404002, "用户不存在")
    DATA_NOT_FOUND = (404003, "数据不存在")

    # 请求方法错误 405
    REQUEST_METHOD_ERROR = (405001, "请求方法不被允许")

    # 业务逻辑错误 422
    BUSINESS_ERROR = (422001, "业务逻辑错误")
    DATA_ALREADY_EXISTS = (422002, "数据已存在")
    DATA_CONFLICT = (422003, "数据冲突")
    OPERATION_NOT_ALLOWED = (422004, "操作不被允许")

    # 服务器错误 5xx
    SYSTEM_ERROR = (500001, "系统内部错误")
    DATABASE_ERROR = (500002, "数据库错误")
    REDIS_ERROR = (500003, "Redis错误")
    EXTERNAL_API_ERROR = (500004, "外部API调用失败")
    SERVICE_UNAVAILABLE = (503001, "服务不可用")

    def __init__(self, code: int, message: str):
        self.code = code
        self.message = message


class HttpResp:
    """HTTP响应状态码类（向后兼容）"""

    SUCCESS = ResponseCode.SUCCESS
    FAILED = ResponseCode.FAILED
    PARAMS_VALID_ERROR = ResponseCode.PARAMS_VALID_ERROR
    TOKEN_INVALID = ResponseCode.TOKEN_INVALID
    NO_PERMISSION = ResponseCode.NO_PERMISSION
    REQUEST_404_ERROR = ResponseCode.REQUEST_404_ERROR
    REQUEST_METHOD_ERROR = ResponseCode.REQUEST_METHOD_ERROR
    SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR


class CustomException(Exception):
    """自定义异常基类"""

    def __init__(
        self,
        code: int,
        message: str,
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        self.code = code
        self.message = message
        self.detail = detail
        self.data = data
        super().__init__(self.message)

    @classmethod
    def from_response_code(
        cls,
        response_code: ResponseCode,
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        """从响应码创建异常"""
        return cls(response_code.code, response_code.message, detail, data)


class BusinessException(CustomException):
    """业务异常"""

    def __init__(
        self,
        message: str = "业务逻辑错误",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.BUSINESS_ERROR.code, message, detail, data)


class ValidationException(CustomException):
    """参数验证异常"""

    def __init__(
        self,
        message: str = "参数验证失败",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.PARAMS_VALID_ERROR.code, message, detail, data)


class AuthenticationException(CustomException):
    """认证异常"""

    def __init__(
        self,
        message: str = "认证失败",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.TOKEN_INVALID.code, message, detail, data)


class AuthorizationException(CustomException):
    """授权异常"""

    def __init__(
        self,
        message: str = "权限不足",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.NO_PERMISSION.code, message, detail, data)


class ResourceNotFoundException(CustomException):
    """资源不存在异常"""

    def __init__(
        self,
        message: str = "资源不存在",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.REQUEST_404_ERROR.code, message, detail, data)


class DataAlreadyExistsException(CustomException):
    """数据已存在异常"""

    def __init__(
        self,
        message: str = "数据已存在",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.DATA_ALREADY_EXISTS.code, message, detail, data)


class DataConflictException(CustomException):
    """数据冲突异常"""

    def __init__(
        self,
        message: str = "数据冲突",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.DATA_CONFLICT.code, message, detail, data)


class DatabaseException(CustomException):
    """数据库异常"""

    def __init__(
        self,
        message: str = "数据库错误",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.DATABASE_ERROR.code, message, detail, data)


class RedisException(CustomException):
    """Redis异常"""

    def __init__(
        self,
        message: str = "Redis错误",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.REDIS_ERROR.code, message, detail, data)


class ExternalAPIException(CustomException):
    """外部API异常"""

    def __init__(
        self,
        message: str = "外部API调用失败",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.EXTERNAL_API_ERROR.code, message, detail, data)


class InvalidImageException(CustomException):
    """图片内容无效异常"""

    def __init__(
        self,
        message: str = "请上传包含头发或头皮的照片进行脱发检测",
        detail: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(ResponseCode.PARAMS_VALUE_ERROR.code, message, detail, data)


def create_error_response(
    code: int,
    message: str,
    detail: Optional[str] = None,
    data: Optional[Dict[str, Any]] = None,
    request_id: Optional[str] = None,
) -> Dict[str, Any]:
    """创建标准错误响应"""
    response = {
        "code": code,
        "message": message,
        "data": data,
        "timestamp": datetime.now().isoformat(),
        "success": False,
    }

    if detail:
        response["detail"] = detail

    if request_id:
        response["request_id"] = request_id

    return response


def create_success_response(
    data: Optional[Dict[str, Any]] = None,
    message: str = "操作成功",
    request_id: Optional[str] = None,
) -> Dict[str, Any]:
    """创建标准成功响应"""
    response = {
        "code": ResponseCode.SUCCESS.code,
        "message": message,
        "data": data,
        "timestamp": datetime.now().isoformat(),
        "success": True,
    }

    if request_id:
        response["request_id"] = request_id

    return response


async def custom_exception_handler(
    request: Request, exc: CustomException
) -> JSONResponse:
    """自定义异常处理器"""
    # 获取请求ID（如果有的话）
    request_id = getattr(request.state, "request_id", None)

    # 记录异常日志
    logger.error(
        f"Custom exception: {exc.__class__.__name__} | "
        f"Code: {exc.code} | Message: {exc.message} | "
        f"Detail: {exc.detail} | Request ID: {request_id}"
    )

    # 创建响应
    response_data = create_error_response(
        code=exc.code,
        message=exc.message,
        detail=exc.detail,
        data=exc.data,
        request_id=request_id,
    )

    return JSONResponse(
        status_code=200, content=response_data  # 统一返回200，通过code字段区分错误
    )


async def http_exception_handler(
    request: Request, exc: Union[HTTPException, StarletteHTTPException]
) -> JSONResponse:
    """HTTP异常处理器"""
    request_id = getattr(request.state, "request_id", None)
    
    # 记录详细的请求信息
    logger.error(
        f"HTTP exception: {exc.status_code} - {exc.detail} | "
        f"Method: {request.method} | URL: {request.url} | "
        f"Client: {request.client.host if request.client else 'Unknown'} | "
        f"Request ID: {request_id}"
    )

    # 根据HTTP状态码映射到自定义错误码
    status_code_mapping = {
        404: ResponseCode.REQUEST_404_ERROR,
        405: ResponseCode.REQUEST_METHOD_ERROR,
        401: ResponseCode.TOKEN_INVALID,
        403: ResponseCode.NO_PERMISSION,
        422: ResponseCode.PARAMS_VALID_ERROR,
        500: ResponseCode.SYSTEM_ERROR,
        503: ResponseCode.SERVICE_UNAVAILABLE,
    }

    response_code = status_code_mapping.get(exc.status_code, ResponseCode.SYSTEM_ERROR)

    response_data = create_error_response(
        code=response_code.code,
        message=response_code.message,
        detail=str(exc.detail),
        request_id=request_id,
    )

    return JSONResponse(
        status_code=200, content=response_data  # 统一返回200，通过code字段区分错误
    )


async def validation_exception_handler(
    request: Request, exc: RequestValidationError
) -> JSONResponse:
    """参数验证异常处理器"""
    request_id = getattr(request.state, "request_id", None)

    logger.error(f"Validation exception: {exc.errors()} | Request ID: {request_id}")

    # 格式化验证错误信息
    error_details = []
    validation_errors = {}

    for error in exc.errors():
        field = ".".join(str(loc) for loc in error["loc"] if loc != "body")
        error_msg = error["msg"]
        error_type = error["type"]

        error_details.append(f"{field}: {error_msg}")
        validation_errors[field] = {
            "message": error_msg,
            "type": error_type,
            "input": error.get("input"),
        }

    response_data = create_error_response(
        code=ResponseCode.PARAMS_VALID_ERROR.code,
        message=ResponseCode.PARAMS_VALID_ERROR.message,
        detail="; ".join(error_details),
        data={"validation_errors": validation_errors},
        request_id=request_id,
    )

    return JSONResponse(status_code=200, content=response_data)


async def general_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    """通用异常处理器"""
    request_id = getattr(request.state, "request_id", None)

    # 记录完整的异常堆栈
    error_traceback = traceback.format_exc()
    logger.error(
        f"Unexpected exception: {type(exc).__name__} - {str(exc)} | "
        f"Request ID: {request_id} | Traceback: {error_traceback}"
    )

    response_data = create_error_response(
        code=ResponseCode.SYSTEM_ERROR.code,
        message=ResponseCode.SYSTEM_ERROR.message,
        detail="服务器内部错误，请稍后重试",
        request_id=request_id,
    )

    return JSONResponse(status_code=200, content=response_data)


# 异常装饰器
def handle_exceptions(
    default_exception: Optional[CustomException] = None, log_level: str = "ERROR"
):
    """异常处理装饰器"""

    def decorator(func):
        async def wrapper(*args, **kwargs):
            try:
                return await func(*args, **kwargs)
            except CustomException:
                # 自定义异常直接抛出
                raise
            except Exception as e:
                # 其他异常转换为默认异常或系统异常
                logger.log(
                    log_level, f"Exception in {func.__name__}: {str(e)}", exc_info=True
                )

                if default_exception:
                    raise default_exception
                else:
                    raise CustomException.from_response_code(
                        ResponseCode.SYSTEM_ERROR,
                        detail=f"在执行 {func.__name__} 时发生错误",
                    )

        return wrapper

    return decorator


# 快速异常抛出函数
def raise_business_error(message: str, detail: Optional[str] = None):
    """抛出业务异常"""
    raise BusinessException(message, detail)


def raise_validation_error(message: str, detail: Optional[str] = None):
    """抛出验证异常"""
    raise ValidationException(message, detail)


def raise_auth_error(message: str = "认证失败", detail: Optional[str] = None):
    """抛出认证异常"""
    raise AuthenticationException(message, detail)


def raise_permission_error(message: str = "权限不足", detail: Optional[str] = None):
    """抛出权限异常"""
    raise AuthorizationException(message, detail)


def raise_not_found_error(message: str = "资源不存在", detail: Optional[str] = None):
    """抛出资源不存在异常"""
    raise ResourceNotFoundException(message, detail)


def raise_data_conflict_error(message: str = "数据冲突", detail: Optional[str] = None):
    """抛出数据冲突异常"""
    raise DataConflictException(message, detail)
